/////////////////////////////////////////////////////////////////////////////////
//
//  Set of simple synchronization classes
//
//  Copyright (c) 2003-2004
//  Andrew Schetinin
//
//  This software is provided "as is" without express or implied warranty,
//  and with no claim as to its suitability for any purpose.
//
//  Permission to use or copy this software for any purpose is hereby granted
//  without fee, provided the above notices are retained on all copies.
//  Permission to modify the code and to distribute modified code is granted,
//  provided the above notices are retained, and a notice that the code was
//  modified is included with the above copyright notice.
//
//  This software accompanies the article "Code that debugs itself"
//  located at http://www.codeproject.com/debug/qafdebug.asp
//
//  You are welcomed to report bugs, send comments and post code modifications
//  to aschetinin@hotmail.com
//
/////////////////////////////////////////////////////////////////////////////////

///
/// @file	CSyncCS.h "../Include/CSyncCS.h"
/// @brief	Simple synchronization classes.
///
///	This file defines simple synchronization classes like critical
///     sections and mutexes.

#ifndef _QAFCSYNC_H_
#define _QAFCSYNC_H_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include <cassert>

#ifdef WIN32
	#include <wtypes.h>
#endif

#include "qafdebug.h"

//
// Using of BEGIN_SYNC and END_SYNC:
//
// // Define it somewhere in your header file
// CAutoCS	m_cs; // critical section object
//
// // And synchronize your code in CPP file
// BEGIN_SYNC( m_cs );
//     .... do whatever you need ...
// END_SYNC;
//

/// Special define for organizing exception-safe synchronized blocks of code.
/// This is the start macro, it must be enclosed with END_SYNC
#define BEGIN_SYNC( sync_obj ) do { CAutoLockCS sync( sync_obj )

/// Special define for organizing exception-safe synchronized blocks of code.
/// This is the end macro
#define END_SYNC } while( FALSE )

#ifdef WIN32

	///
	typedef LPCTSTR Q_LPCTSTR;

#else

	/// typedef for Linux systems
	typedef unsigned int CRITICAL_SECTION;

	/// typedef for Linux systems
	typedef void * HANDLE;

	/// Timeout value
	const unsigned int INFINITE = 0xFFFFFFFF;

	///
	typedef const char * Q_LPCTSTR;
	
#endif

/// A simple synchronization class, designed to be used as an automatic variable.
/// Once its instance is instanciated, it locks its visibility scope.
class CSyncCS
{
public:

	/// Flag that the critical section object must be constructed or destroyed
	enum CSOP { CS_NOP = 0, CS_CREATE = 1, CS_DESTROY = 2 };

	/// Constructor creates and enters the critical section
	CSyncCS( CRITICAL_SECTION * pcs, const CSOP csop = CS_NOP )
	{
		m_pcs = pcs;
		m_csop = csop;
		#ifdef WIN32
		if( CS_CREATE == m_csop )
			InitializeCriticalSection( m_pcs );
		EnterCriticalSection( m_pcs );
		#endif
	}

	/// Destructor leaves and destroys the critical section
	~CSyncCS()
	{
		#ifdef WIN32
		LeaveCriticalSection( m_pcs );
		if( CS_DESTROY == m_csop )
			DeleteCriticalSection( m_pcs );
		#endif
	}

private:

	/// Critical section is used for synchronization
	CRITICAL_SECTION * m_pcs;

	/// Flag that the critical section object must be constructed or destroyed
	CSOP m_csop;

};

/// Improved version of CSyncCS class
class CAutoCS
{
public:

	/// Constructor creates the critical section
	CAutoCS()
	{
		#ifdef WIN32
		InitializeCriticalSection( &m_cs );
		#endif
	}

	/// Destructor destroys the critical section
	~CAutoCS()
	{
		#ifdef WIN32
		DeleteCriticalSection( &m_cs );
		#endif
	}

	/// Enter the critical section
	bool Lock()
	{
		#ifdef WIN32
		EnterCriticalSection( &m_cs );
		#endif
		return true;
	}

	/// Leave the critical section
	void Unlock()
	{
		#ifdef WIN32
		LeaveCriticalSection( &m_cs );
		#endif
	}

private:

	/// Critical section is used for synchronization
	CRITICAL_SECTION m_cs;

};

/// Automatic locker for different synchronization objects.
template <class SyncClass>
class CLockCS
{
public:

	/// Constructor locks the synchronization object
	CLockCS( SyncClass &SyncObj ) : m_SyncObj(SyncObj), m_bLocked(false)
	{
		m_bLocked = m_SyncObj.Lock();
	}

	/// Constructor locks the synchronization object
	CLockCS( SyncClass &SyncObj, unsigned int uiTimeout )
		: m_SyncObj(SyncObj), m_bLocked(false)
	{
		m_bLocked = m_SyncObj.Lock( uiTimeout );
	}

	/// Destructor unlocks the synchronization object
	~CLockCS()
	{
		if( m_bLocked )
		{
			m_SyncObj.Unlock();
			m_bLocked = false;
		}
	}

	/// Return true if the lock succeeded
	bool isLocked() const
	{
		return m_bLocked;
	}

private:

	/// Reference to the synchronization object
	SyncClass & m_SyncObj;

	/// Flag that it is locked
	bool m_bLocked;

};

/// Shortcut for autolocking critical section.
typedef CLockCS<CAutoCS> CAutoLockCS;

/// Simple mutex class that is compatible to the CLockCS class.
class CMutex
{
public:

	/// default constructor
	CMutex() : m_hMutex(NULL), m_bOwned(false)
	{
		#ifdef WIN32
		m_hMutex = CreateMutex( NULL, FALSE, NULL );
		if( Q_ASSERT( NULL != m_hMutex ) )
			m_bOwned = true;
		#endif
	}

	/// default constructor
	CMutex( Q_LPCTSTR szMutexName ) : m_hMutex(NULL), m_bOwned(false)
	{
		#ifdef WIN32
		m_hMutex = CreateMutex( NULL, FALSE, szMutexName );
		if( Q_ASSERT( NULL != m_hMutex ) )
			m_bOwned = true;
		#endif
	}

	/// copy constructor
	CMutex( HANDLE hMutex ) : m_hMutex(hMutex), m_bOwned(false)
	{
		Q_ASSERT( NULL != m_hMutex );
	}

	/// copy constructor
	CMutex( const CMutex & obj ) : m_hMutex(obj.m_hMutex), m_bOwned(false)
	{
		Q_ASSERT( NULL != m_hMutex );
	}

	/// destructor
	~CMutex()
	{
		if( m_bOwned )
		{
			#ifdef WIN32
			Q_ASSERT( CloseHandle( m_hMutex ) );
			#endif
			m_hMutex = NULL;
		}
	}

	/// get the handle of the mutex. this handle must not be closed
	HANDLE GetHandle() const
	{
		return m_hMutex;
	}

	/// Enter the critical section
	bool Lock( unsigned long dwMilliseconds = INFINITE )
	{
		#ifdef WIN32
		DWORD dwRet = WaitForSingleObject( m_hMutex, dwMilliseconds );
		return Q_ASSERT( WAIT_OBJECT_0 == dwRet );
		#else
		return true;
		#endif
	}

	/// Leave the critical section
	void Unlock()
	{
		#ifdef WIN32
		Q_ASSERT( ReleaseMutex( m_hMutex ) );
		#endif
	}

protected:
private:

	/// Mutex handle
	HANDLE m_hMutex;

	/// Flag if this handle is owned by the object
	bool m_bOwned;

	/// assignment operator
	CMutex & operator=( const CMutex & obj )
	{
		Q_ASSERT( false );
		assert( false );
		if( this != &obj )
		{
			// copy the members
		}
		return *this;
	}

};

/// Simple semaphore class that is compatible to the CLockCS class.
class CSemaphore
{
public:

	/// regular constructor
	CSemaphore( const int nMaxCount ) : m_hSemaphore(NULL), m_bOwned(false)
	{
		#ifdef WIN32
		m_hSemaphore = CreateSemaphore( NULL, nMaxCount, nMaxCount, NULL );
		if( Q_ASSERT( NULL != m_hSemaphore ) )
			m_bOwned = true;
		#endif
	}

	/// default constructor
	CSemaphore( Q_LPCTSTR szSemaphoreName, const int nMaxCount ) :
		m_hSemaphore(NULL), m_bOwned(false)
	{
		#ifdef WIN32
		m_hSemaphore = CreateSemaphore( NULL, nMaxCount, nMaxCount,
			szSemaphoreName );
		if( Q_ASSERT( NULL != m_hSemaphore ) )
			m_bOwned = true;
		#endif
	}

	/// copy constructor
	CSemaphore( HANDLE hSemaphore, bool bMakeOwn ) :
		m_hSemaphore(NULL), m_bOwned(false)
	{
		#ifdef WIN32
		if( bMakeOwn )
		{
			HANDLE hProcess = GetCurrentProcess();
			m_bOwned = TRUE == DuplicateHandle( hProcess, hSemaphore,
				hProcess, &m_hSemaphore, 0, FALSE, DUPLICATE_SAME_ACCESS );
		}
		Q_ASSERT( NULL != m_hSemaphore );
		#endif
	}

	/// copy constructor
	CSemaphore( const CSemaphore & obj ) : m_hSemaphore(NULL),
		m_bOwned(false)
	{
		#ifdef WIN32
		HANDLE hProcess = GetCurrentProcess();
		m_bOwned = TRUE == DuplicateHandle( hProcess, obj.GetHandle(),
			hProcess, &m_hSemaphore, 0, FALSE, DUPLICATE_SAME_ACCESS );
		Q_ASSERT( NULL != m_hSemaphore );
		#endif
	}

	/// assignment operator
	CSemaphore & operator=( const CSemaphore & obj )
	{
		#ifdef WIN32
		if( this != &obj )
		{
			clear();
			HANDLE hProcess = GetCurrentProcess();
			m_bOwned = TRUE == DuplicateHandle( hProcess, obj.GetHandle(),
				hProcess, &m_hSemaphore, 0, FALSE, DUPLICATE_SAME_ACCESS );
			Q_ASSERT( NULL != m_hSemaphore );
		}
		#endif
		return *this;
	}

	/// destructor
	~CSemaphore()
	{
		clear();
	}

	/// get the handle of the mutex. this handle must not be closed
	HANDLE GetHandle() const
	{
		return m_hSemaphore;
	}

	/// Enter the critical section
	bool Lock( unsigned long dwMilliseconds = INFINITE )
	{
		#ifdef WIN32
		DWORD dwRet = WaitForSingleObject( m_hSemaphore, dwMilliseconds );
		return Q_CHECK( WAIT_OBJECT_0, dwRet );
		#else
		return true;
		#endif
	}

	/// Leave the critical section
	void Unlock()
	{
		#ifdef WIN32
		Q_ASSERT( ReleaseSemaphore( m_hSemaphore, 1, NULL ) );
		#endif
	}

protected:
private:

	/// Semaphore handle
	HANDLE m_hSemaphore;

	/// Flag if this handle is owned by the object
	bool m_bOwned;

	/// Clear and free handles
	void clear()
	{
		#ifdef WIN32
		if( m_bOwned )
			Q_ASSERT( CloseHandle( m_hSemaphore ) );
		#endif
		m_hSemaphore = NULL;
		m_bOwned = false;
	}

	/// default constructor - disabled
	CSemaphore() : m_hSemaphore(NULL), m_bOwned(false) {}

};

/// Shortcut for autolocking critical section.
typedef CLockCS<CSemaphore> CSemaphoreLock;



///////////////////////////////////////////////
// END OF FILE
///////////////////////////////////////////////
#endif
